/*
 * Copyright (c) 2018-2023 北京华恒盛世科技有限公司
 * QUICKPOOL is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *     http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
*/

#include "xdrbase.h"
#include "common.h"
#include "tclinfo.h"
#include "quickpool.h"
#include "resreq.h"
#include "lib.h"

/**
 * @brief       check if it is ready to resume the job.
 *
 * @param[in]       jobid           #1: jobid for log message
 * @param[in]       num             #2: number of execution node
 * @param[in]       thresholds      #3: job load thresholds
 * @param[in]       loads           #4: load of each execution node
 * @param[in|out]   reason          #5: reason of suspending the job
 * @param[in|out]   subreasons      #6: resource index
 * @param[in]       resumecond      #7: condition for resuming the job
 * @param[in]       tclnode         #8: TclNode of each execution node
 * @param[in]       info            #9: quickpool resource info
 *
 * @retval      TRUE         #1: ready for resume
 * @retval      FALSE        #2: not ready
 *
 * @note        If job is not ready for resume, suspend reason and subreason are set.
 */
bool_t
check_resume_byload(JOBID_T jobid, int num, struct thresholds thresholds,
                    struct QuickPoolHostInfo **loads, int *reason, int *subreasons,
                    struct requirement *resumecond, struct Tcl_Node *tclnode,
                    struct quickpoolInfo *info)
{
    int i, j;
    int resume = TRUE;
    int lastReason = *reason;

    log_message(DEBUG, EXECUTE, "%s: reason=%x, subreasons=%d, num threshold=%d", 
                __func__, *reason, *subreasons, thresholds.num_threshold);

    if (0 >= num) {
        return FALSE;
    }

    char strJobId[32];
    for (j=0; j<num; j++) {

        if (NULL == loads[j]->load) {
             continue;
        }

        if (STATUS_ISUNAVAIL(loads[j]->jm_status)) {
            resume = FALSE;
            *reason = SUSPEND_REASON_NODEUNAVAIL;
        }

        if (!resume) {
            log_message(DEBUG, EXECUTE, "%s: Can't resume job %s; *reason=%x",
	                __func__, jobid_2string(jobid, strJobId), *reason);

            if (lastReason & SUSPEND_REASON_JSLOCK) {
                *reason |= SUSPEND_REASON_JSLOCK;
            }
            return FALSE;
        }

        if (NULL != resumecond) {

            if (1 == evaluate(resumecond->select, &tclnode[j], DFT_FROMTYPE)) {
                resume = TRUE;
                break;
            }

            resume = FALSE;
            *reason = SUSPEND_REASON_RESUMECOND;

            log_message(DEBUG, EXECUTE, "%s: Can't resume job %s; reason=%x",
                        __func__, jobid_2string(jobid, strJobId), *reason);

            if (lastReason & SUSPEND_REASON_JSLOCK) {
                *reason |= SUSPEND_REASON_JSLOCK;
            }

            return FALSE;
        }

        if (loads[j]->load[R15M] > thresholds.load_schedule[j][R15M]) {
            resume = FALSE;
            *reason = SUSPEND_REASON_LOADSTOP;
            *subreasons = R15M;
        } else if (loads[j]->load[R5M] > thresholds.load_schedule[j][R5M]) {
            resume = FALSE;
            *reason = SUSPEND_REASON_LOADSTOP;
            *subreasons = R5M;
        } else if (loads[j]->load[R1M] > thresholds.load_schedule[j][R1M]) {
            resume = FALSE;
            *reason = SUSPEND_REASON_LOADSTOP;
            *subreasons = R1M;
        } else if (loads[j]->load[UT] > thresholds.load_schedule[j][UT]) {
            resume = FALSE;
            *reason = SUSPEND_REASON_LOADSTOP;
            *subreasons = UT;
        } else if (loads[j]->load[PG] > thresholds.load_schedule[j][PG]) {
            resume = FALSE;
            *reason = SUSPEND_REASON_LOADSTOP;
            *subreasons = PG;
        } else if (loads[j]->load[IO] > thresholds.load_schedule[j][IO]) {
            resume = FALSE;
            *reason = SUSPEND_REASON_LOADSTOP;
            *subreasons = IO;
        } else if (loads[j]->load[MEM] < thresholds.load_schedule[j][MEM]) {
            resume = FALSE;
            *reason = SUSPEND_REASON_LOADSTOP;
            *subreasons = MEM;
        } else if (loads[j]->load[TMP] < thresholds.load_schedule[j][TMP]) {
            resume = FALSE;
            *reason = SUSPEND_REASON_LOADSTOP;
            *subreasons = TMP;
        } else if (loads[j]->load[SWP] < thresholds.load_schedule[j][SWP]) {
            resume = FALSE;
            *reason = SUSPEND_REASON_LOADSTOP;
            *subreasons = SWP;
        }

        for (i = MEM + 1; resume && i < MIN(thresholds.num_index, info->num_index); i++) {
            if (loads[j]->load[i] >= INFINIT_LOAD
                || loads[j]->load[i] <= -INFINIT_LOAD
                || thresholds.load_schedule[j][i] >= INFINIT_LOAD
                || thresholds.load_schedule[j][i] <= -INFINIT_LOAD)
                continue;

            if (info->resource_table[i].order_type == INCR) {
                if (loads[j]->load[i] > thresholds.load_schedule[j][i]) {
                    resume = FALSE;
                    *reason = SUSPEND_REASON_LOADSTOP;
                    *subreasons = i;
                }
            } else {
                if (loads[j]->load[i] < thresholds.load_schedule[j][i]) {
                    resume = FALSE;
                    *reason = SUSPEND_REASON_LOADSTOP;
                    *subreasons = i;
                }
            }
        }
    }

    if (lastReason & SUSPEND_REASON_JSLOCK) {
        *reason |= SUSPEND_REASON_JSLOCK;
    }

    if (!resume) {
        log_message(DEBUG, EXECUTE, "%s: Can't resume job %s; reason=%x, subreasons=%d",
                    __func__, jobid_2string(jobid, strJobId), *reason, *subreasons);
    }

    return (resume);
} // end function check_resume_byload

/**
 * @brief       Duplicate job threshold.
 *
 * @param[out]  dst           #1: duplicate job threshold to
 * @param[in]   src           #2: duplicate job threshold from
 */
void
dup_thresholds(struct thresholds *dst, struct thresholds *src)
{
    dst->load_schedule = (float **)calloc(src->num_threshold, sizeof(float *));
    if (NULL == dst->load_schedule) {
        return;
    }

    dst->load_stop = (float **)calloc(src->num_threshold, sizeof(float *));
    if (NULL == dst->load_stop) {
        return;
    }

    unsigned int i;
    for (i=0; i<src->num_threshold; i++) {

        dst->load_schedule[i] = (float *)calloc(src->num_index, sizeof(float));
        if (NULL == dst->load_schedule[i]) {
            return;
        }

        dst->load_stop[i] = (float *)calloc(src->num_index, sizeof(float));
        if (NULL == dst->load_stop[i]) {
            return;
        }
    }

    for (i=0; i<src->num_threshold; i++) {

        unsigned int j;
        for (j=0; j<src->num_index; j++) {
            dst->load_schedule[i][j] = src->load_schedule[i][j];
            dst->load_stop[i][j] = src->load_stop[i][j];
        }
    }

    dst->num_index = src->num_index;
    dst->num_threshold = src->num_threshold;

    return;
} // end function dup_thresholds
